// Copyright (c) 2010 Manuel Peinado Gallego <manuel.peinado@gmail.com>
// Distributed under the MIT license

// GizmoManager.h : interface of the GizmoManager class
//

#pragma once

#include "SelectionManager.h"
#include "Dim.h"
#include "Draggable.h"
class Gizmo;
class View3d;
class Transformable;

// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-

// Rotation, scale and translation manipulations operate on a given frame. For
// instance, the translation manip will move the controlled node in one (or 
// two) of the axes of this frame. If the manipulation frame is NODE, it will
// move the node on its own local axes. If the manipulation frame is GLOBAL,
// it will move the node in the global axes. If it is PARENT, it will move the
// node in the axes of its parent. Finally, if the frame is VIEW, it will move 
// the node in the camera axes (screen horizontal and vertical directions). 
// Note that screen Z is supported, but doesn't make much sense for 
// translation or scaling because no change is possible. It does make sense
// for rotation, though, where it means "rotate about the axes perpendicular
// the screen plane). 
//    For rotation manipulations, the axes in this frame are interpreted as the
// axes of rotation. Finally, for scaling manipulations they are interpreted
// as the axes along which the node is scaled.

BEGIN_ENUM(GizmoAxesType)
    NODE, WORLD, PARENT, VIEW, OTHER_NODE
END_ENUM(GizmoAxesType)

// The manipulation frame defines which axes are controlled by the manipulator.
// But the rotation and scaling manipulators have another opportunity for
// customization: about which point do we want to rotate or scale the node? We
// call this point the "pivot". A pivot can be used for the translation manip.
// as well but in that case it's simply ignored. The default pivot type is 
// "NODE". The second type is "GLOBAL", which means that the pivot is the 
// world's origin (the global point (0,0,0)). Next, the "PARENT" type means 
// that the pivot is the center of the controlled node's parent. Finally,
// if the pivot type is "OTHER_NODE", the center of a second node is used
// as the pivot. This second node is given by the "m_pivotNode" member var.
//   For all types, the pivot point is "frozen" at the beginnig of the drag
// cycle.

BEGIN_ENUM(GizmoPivotType)
    NODE, WORLD, PARENT, OTHER_NODE
END_ENUM(GizmoPivotType)

// There are three ways to manipulate a node: move it around (TRANSLATE),
// change its orientation with respect to some axis (ROTATE) and change
// its SCALE.

BEGIN_ENUM(GizmoType)
    NONE, TRANSLATE, ROTATE, SCALE
END_ENUM(GizmoType)


// ----------------------------------------------------------------------------
// Class "GizmoManager"
// ----------------------------------------------------------------------------
//  Class that takes care of maintaining the transformation gizmo up to date.
//  For instance, when a transformable node becomes selected, this class 
//  creates a new gizmo of the appropriate type (translation, rotation, etc)
//  and makes it visible. Similarly, when the transformable node is no longer
//  selected, this class removes the gizmo
// ----------------------------------------------------------------------------
//
// ----------------------------------------------------------------------------

class GizmoManager : public SelectionChangeListener
{
public:

    GizmoManager();
    ~GizmoManager();

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    // Associate a view with this gizmo manager. This should be done as
    // soon as possible (ideally right after construction) because the manager 
    // is useless without a view. 
    void setView(View3d * view);
    View3d* getView() const { return m_view; }

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    // Change/read the type of gizmo. Valid types include translation, 
    // rotation, and scale
    void setGizmoType(GizmoType id);
    GizmoType getGizmoType() const { return m_type; }

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    // Set/get the type of axes used by the active gizmo. Valid types include
    // WORLD (where the global axes (1,0,0),(0,0,1) and (0,0,1) are used for
    // the gizmo, NODE, or OTHER_NODE (where the axes of another node's 
    // frame are used).
    void setAxesType(GizmoAxesType orient);
    GizmoAxesType getAxesType() const;

    // These functions can only be called when the axes type is "OTHER_NODE".
    // In that case, they set/read the node that is used as a source of the
    // gizmo axes
    void setAxesNode(Node* node);
    Node* getAxesNode() const;

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    // Set/get the type of pivot used by the active gizmo. Valid types include
    // WORLD (where the global origin (0,0,0) is used as the pivot for the
    // gizmo, NODE, or OTHER_NODE (where the center of another node's frame 
    // is used).
    void setPivotNode(GizmoPivotType id);
    GizmoPivotType getPivotType() const;

    // These functions can only be called when the pivot type is "OTHER_NODE".
    // In that case, they set/read the node that is used as a source of the
    // gizmo pivot
    void setPivotNode(Node* node);
    Node* getPivotNode() const;


    // Cycles between NODE, GLOBAL and PARENT. If the current pivot is 
    // OTHER_NODE, switches to NODE
    void cyclePivot();

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    // A more direct access for the gizmo to the dragged node, which 
    // otherwise would have to query the selection manager
    Transformable* getDraggedItem() const { return m_draggedItem; }

protected:

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    // The node that is controlled by the active gizmo
    Transformable* m_draggedItem;

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
    
    // The view that controls this dragger (drag events will originate from it)
    View3d * m_view;

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    // The type of the active gizmo (rotatioin, translation, ...)
    // This field is not currently used by anyone
    GizmoType m_type;

    // Pointer to the active gizmo. Changes when setGizmoType is called
    ref<Gizmo> m_gizmo;

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
    
    // Called internally when the gizmo type changes
    void replaceGizmo(ref<Gizmo> newGizmo);
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
    
    // SelectionListener override. Used by the gizmo manager to create a new
    // gizmo and add it to the view when the newly selected node is 
    // transformable
    void onSelectionChanged(SelectionManager * sender) override;

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    // See comments on member function setPivotType
    GizmoPivotType m_pivotId;
    Node* m_pivotNode;

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    // See comments on member function setAxesType
    GizmoAxesType m_axesType;
    Node* m_axesNode;

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    friend class Gizmo;
};



